﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.IO;

using ERPStore.Extensions;

using ERPStore.Payment.Paypal.com.paypalobjects;

using CloudSoft.Extensions;

namespace ERPStore.Payment.Paypal.Services
{
	public class PaypalExpressService : ERPStore.Services.IPaymentService
		, ERPStore.Payment.Paypal.Services.IPaypalExpressService
	{
		private PayPalAPISoapBinding m_Service;
		private PayPalAPIAASoapBinding m_ServiceAA;

		public PaypalExpressService(ERPStore.Logging.ILogger logger
			, Models.Settings settings)
		{
			this.Logger = logger;
			this.Settings = settings;
		}

		#region Properties

		protected ERPStore.Logging.ILogger Logger { get; private set; }
		protected Models.Settings Settings { get; private set; }

		public string Name
		{
			get
			{
				return "paypal";
			}
		}



		public string ConfirmationViewName
		{
			get
			{
				return Settings.ConfirmationViewName;
			}
		}

		public string FinalizedViewName
		{
			get
			{
				return Settings.FinalizedViewName;
			}
		}

		public string ConfirmationRouteName
		{
			get
			{
				return Settings.ConfirmationRouteName;
			}
		}

		public string FinalizedRouteName
		{
			get
			{
				return Settings.FinalizedRouteName;
			}
		}

		#endregion

		private void SetupWebService()
		{
			m_Service = new PayPalAPISoapBinding();
			m_ServiceAA = new PayPalAPIAASoapBinding();

			m_Service.Url = Settings.CheckoutExpressServiceEndPointUrl;
			m_ServiceAA.Url = Settings.CheckoutExpressServiceEndPointUrl;

			m_Service.RequesterCredentials = new CustomSecurityHeaderType();
			m_Service.RequesterCredentials.Credentials = new UserIdPasswordType();
			m_Service.RequesterCredentials.Credentials.Username = Settings.ApiUserName;
			m_Service.RequesterCredentials.Credentials.Password = Settings.ApiUserPassword;
			m_Service.RequesterCredentials.Credentials.Subject = "";

			m_ServiceAA.RequesterCredentials = new CustomSecurityHeaderType();
			m_ServiceAA.RequesterCredentials.Credentials = new UserIdPasswordType();
			m_ServiceAA.RequesterCredentials.Credentials.Username = Settings.ApiUserName;
			m_ServiceAA.RequesterCredentials.Credentials.Password = Settings.ApiUserPassword;
			m_ServiceAA.RequesterCredentials.Credentials.Subject = "";

			var cert =   GetCertificate(Settings.ApiCertLocation, Settings.ApiCertPassword);

			m_ServiceAA.ClientCertificates.Clear();
			m_ServiceAA.ClientCertificates.Add(cert);
			m_Service.ClientCertificates.Clear();
			m_Service.ClientCertificates.Add(cert);

			m_Service.PreAuthenticate = true;
			m_ServiceAA.PreAuthenticate = true;
		}

		public string SetExpressCheckout(ERPStore.Models.OrderCart cart, ERPStore.Models.User user, CurrencyCodeType currencyCodeType, string orderDescription, string returnUrl, string cancelUrl, string callbackUrl)
		{
			SetupWebService();

			var setRequest = new SetExpressCheckoutReq();
			var checkoutExpress = new SetExpressCheckoutRequestDetailsType();

			setRequest.SetExpressCheckoutRequest = new SetExpressCheckoutRequestType();

			checkoutExpress.OrderTotal = GetBasicAmount(cart.GrandTotalWithTax, currencyCodeType);
			checkoutExpress.ReturnURL = returnUrl;
			checkoutExpress.CancelURL = cancelUrl;
			// checkoutExpress.MaxAmount = checkoutExpress.OrderTotal;
			checkoutExpress.OrderDescription = orderDescription;
			checkoutExpress.NoShipping = "0";
			checkoutExpress.Custom = cart.Code;
			checkoutExpress.BuyerEmail = user.Email;

			// Adresse de livraison
			//checkoutExpress.Address = new AddressType();
			//checkoutExpress.Address.AddressStatus = AddressStatusCodeType.Confirmed;
			//checkoutExpress.Address.CityName = cart.DeliveryAddress.City;
			//checkoutExpress.Address.Country =  (CountryCodeType) Enum.Parse(typeof(CountryCodeType), cart.DeliveryAddress.Country.Alpha2, true);
			//checkoutExpress.Address.CountryName = cart.DeliveryAddress.Country.LocalizedName;
			//checkoutExpress.Address.CountrySpecified = true;
			//checkoutExpress.Address.Name = cart.DeliveryAddress.RecipientName;
			//checkoutExpress.Address.Phone = user.PhoneNumber;
			//checkoutExpress.Address.PostalCode = cart.DeliveryAddress.ZipCode;
			//checkoutExpress.Address.StateOrProvince = cart.DeliveryAddress.Region;
			//checkoutExpress.Address.Street1 = cart.DeliveryAddress.Street;

			checkoutExpress.PaymentAction = PaymentActionCodeType.Sale;
			checkoutExpress.PaymentActionSpecified = true;

			if (!Settings.PageStyle.IsNullOrTrimmedEmpty())
			{
				checkoutExpress.PageStyle = Settings.PageStyle;
			}

			checkoutExpress.PaymentDetails = new PaymentDetailsType[1];
			var paymentDetails = new PaymentDetailsType();
			paymentDetails.OrderTotal = GetBasicAmount(cart.GrandTotalWithTax, CurrencyCodeType.EUR);
			paymentDetails.ShippingTotal = GetBasicAmount(cart.ShippingFeeTotalWithTax, CurrencyCodeType.EUR);
			paymentDetails.TaxTotal = GetBasicAmount(cart.GrandTaxTotal, CurrencyCodeType.EUR);
			paymentDetails.Custom = cart.Code;
			paymentDetails.ItemTotal = GetBasicAmount(cart.GrandTotal, CurrencyCodeType.EUR);
			// paymentDetails.ButtonSource = "";

			var detailItems = new PaymentDetailsItemType[cart.Items.Count];
			foreach (var item in cart.Items)
			{
				var detail = new PaymentDetailsItemType();
				detail.Amount = GetBasicAmount(item.SalePrice.ValueWithTax, CurrencyCodeType.EUR);
				detail.Description = item.Product.ShortDescription;
				// detail.ItemURL = item.Product.get
				detail.Name = item.Product.Title;
				detail.Number = item.Product.Code;
				detail.Quantity = item.Quantity.ToString();
				detail.Tax = GetBasicAmount(item.GrandTaxTotal, CurrencyCodeType.EUR);
				detailItems[cart.Items.IndexOf(item)] = detail;
			}
			paymentDetails.PaymentDetailsItem = detailItems;

			//if (!LogoUrl.IsNullOrTrimmedEmpty())
			//{
			//    checkoutExpress.cppheaderimage = LogoUrl;
			//}

			setRequest.SetExpressCheckoutRequest.Version = Configuration.ConfigurationSettings.AppSettings["apiVersion"];
			setRequest.SetExpressCheckoutRequest.SetExpressCheckoutRequestDetails = checkoutExpress;

			SetExpressCheckoutResponseType setResponse = null;
			string result = null;
			int loop = 0;
			while (true)
			{
				if (loop > 3)
				{
					Logger.Notification("Paypal.SetExpressCheckout timeout more 3 times");
					return result;
				}
				try
				{
					setResponse = m_ServiceAA.SetExpressCheckout(setRequest);
					break;
				}
				catch (Exception ex)
				{
					if (ex.Message.IndexOf("The operation has timed out", StringComparison.InvariantCultureIgnoreCase) == -1)
					{
						Logger.Error(ex);
						return result;
					}
				}
				loop++;
			}

			string errorList = null;
			var responseResult = IsResponseSuccess(setResponse, out errorList);
			if (responseResult)
			{
				result = setResponse.Token;
			}
			else
			{
				Logger.Error(errorList);
			}
			return result;
		}

		public GetExpressCheckoutDetailsResponseType GetExpressCheckoutDetails(string token)
		{
			SetupWebService();

			var req = new GetExpressCheckoutDetailsReq();
			var requestType = new GetExpressCheckoutDetailsRequestType();

			requestType.Token = token;
			requestType.Version = Settings.ApiVersion;

			req.GetExpressCheckoutDetailsRequest = requestType;

			var response = m_ServiceAA.GetExpressCheckoutDetails(req);

			return response;
		}

		public virtual string DoExpressCheckoutPaymentSale(string token, string payerId, ERPStore.Models.Order order, CurrencyCodeType currencyCodeType, string orderDescription, string invoiceCode)
		{
			SetupWebService();

			var paymentDetail = new DoExpressCheckoutPaymentRequestDetailsType();

			paymentDetail.Token = token;
			paymentDetail.PayerID = payerId;
			paymentDetail.PaymentAction = PaymentActionCodeType.Sale;
			paymentDetail.PaymentActionSpecified = true;

			var detail = new PaymentDetailsType();

			detail.OrderTotal = GetBasicAmount(order.GrandTotalWithTax, currencyCodeType); ;
			// detail.ItemTotal = GetBasicAmount(order.GrandTotal, currencyCodeType);
			detail.OrderDescription = orderDescription;
			if (order.ShippingFee != null)
			{
				// detail.ShippingTotal = GetBasicAmount(order.ShippingFee.TotalWithTax, currencyCodeType);
			}
			detail.HandlingTotal = GetBasicAmount(0, currencyCodeType);
			// detail.TaxTotal = GetBasicAmount(order.GrandTaxTotal, currencyCodeType);
			detail.InvoiceID = invoiceCode;
			detail.ButtonSource = "ERPStore";
			// detail.NotifyURL = NotificationUrl;

			paymentDetail.PaymentDetails = new PaymentDetailsType[] { detail };

			var payment = new DoExpressCheckoutPaymentRequestType();

			payment.DoExpressCheckoutPaymentRequestDetails = paymentDetail;
			payment.Version = Settings.ApiVersion;

			var request = new DoExpressCheckoutPaymentReq();

			request.DoExpressCheckoutPaymentRequest = payment;

			var response = m_ServiceAA.DoExpressCheckoutPayment(request);

			string errorList = null;
			var responseResult = IsResponseSuccess(response, out errorList);
			if (responseResult)
			{
				if (!response.DoExpressCheckoutPaymentResponseDetails.PaymentInfo.IsNullOrEmpty())
				{
					return response.DoExpressCheckoutPaymentResponseDetails.PaymentInfo[0].TransactionID;
				}
				throw new Exception("DoExpressCheckout have no transactionId");
			}
			var ex = new Exception("DoExpressCheckout Payment failed");
			ex.Data.Add("response", response.SerializeToXml());
			ex.Data.Add("errorList", errorList);
			throw ex;
		}

		public string GetPaypalCheckoutUrl(string token)
		{
			return string.Format("{0}&token={1}", Settings.PaypalCheckoutUrl, token);
		}

		public bool IsAllowedFor(ERPStore.Models.OrderCart cart, ERPStore.Models.UserPrincipal principal)
		{
			return true;
		}

		#region Utils

		protected X509Certificate2 GetCertificate(string location, string password)
		{
			byte[] buffer;

			using (var fs = File.Open(location, FileMode.Open, FileAccess.Read))
			{
				buffer = new byte[fs.Length];
				int count = fs.Read(buffer, 0, buffer.Length);
				fs.Close();
			}
			var cert = new X509Certificate2(buffer, password, X509KeyStorageFlags.MachineKeySet);

			return cert;
		}

		protected BasicAmountType GetBasicAmount(decimal amount, CurrencyCodeType currencyCodeType)
		{
			var bAmount = new BasicAmountType();
			var formatProvider = new System.Globalization.CultureInfo("en-us");
			bAmount.Value = amount.ToString("#,##0.00", formatProvider);

			bAmount.currencyID = currencyCodeType;
			return bAmount;
		}

		public bool IsResponseSuccess(AbstractResponseType abstractResponse, out string errorList)
		{
			if (abstractResponse == null)
			{
				errorList = "Response inconnue";
				return false;
			}
			// First, check the Obvious.  Make sure Ack is not Success
			//if (!abstractResponse.Ack.Equals(AckCodeType.Success))
			//{
			//    errorList = "Bad ack response";
			//    return false;
			//}

			errorList = null;
			// Check to make sure there is nothing in the Errors Collection
			if (abstractResponse.Errors != null
				&& abstractResponse.Errors.Length > 0)
			{
				// Do something with the errors
				errorList = "ERROR: ";
				for (int i = 0; i < abstractResponse.Errors.Length; i++)
				{
					// Access each error abstractResponse.Errors[i] and do something
					errorList += abstractResponse.Errors[i].LongMessage + " (" + abstractResponse.Errors[i].ErrorCode + ")" + Environment.NewLine;
				}
			}
			return errorList.IsNullOrTrimmedEmpty();
		}

		#endregion
	}
}
